// 此c文件根据PD_Micro文件移植而来
// 简化了接口和信息
#include <stdint.h>
#include <string.h>
#include "PD_UFP.h"
#include "fusb302Port.h"
#include "PD_UFP_Log.h"

#define t_PD_POLLING 100
#define t_TypeCSinkWaitCap 350
#define t_RequestToPSReady 580 // combine t_SenderResponse and t_PSTransition
#define t_PPSRequest 5000      // must less than 10000 (10s)

uint16_t ready_voltage = 0;
uint16_t ready_current = 0;
uint16_t PPS_voltage_next = 0;
uint8_t PPS_current_next = 0;
uint8_t status_initialized = 0;
uint8_t status_src_cap_received = 0;
status_power_t status_power = STATUS_POWER_NA;
uint16_t time_polling = 0;
uint16_t time_wait_src_cap = 0;
uint16_t time_wait_ps_rdy = 0;
uint16_t time_PPS_request = 0;
uint8_t get_src_cap_retry_count = 0;
uint8_t wait_src_cap = 0;
uint8_t wait_ps_rdy = 0;
uint8_t send_request = 0;
FUSB302_dev_t FUSB302;
PD_protocol_t protocol;

float get_voltage(void)
{
    if (status_power == STATUS_POWER_TYP)
    {
        return (ready_voltage * (50.0 * 0.001));
    }
    else if (status_power == STATUS_POWER_PPS)
    {
        return (ready_voltage * (20.0 * 0.001));
    }
    else
    {
        return 0.0;
    }
    // return ready_voltage;
} // Voltage in 50mV units, 20mV(PPS)
float get_current(void)
{
    if (status_power == STATUS_POWER_TYP)
    {
        return (ready_current * (10.0 * 0.001));
    }
    else if (status_power == STATUS_POWER_PPS)
    {
        return (ready_current * (50.0 * 0.001));
    }
    else
    {
        return 0.0;
    }
    //return ready_current;
} // Current in 10mA units, 50mA(PPS)
bool is_power_ready(void)
{
    return status_power == STATUS_POWER_TYP;
}
bool is_PPS_ready(void)
{
    return status_power == STATUS_POWER_PPS;
}
status_power_t get_power_status(void)
{
    return status_power;
}
bool is_ps_transition(void)
{
    return send_request || wait_ps_rdy;
}

PD_UFP_ErrTable PD_UFPInit(uint16_t PPS_voltage, uint8_t PPS_current, enum PD_power_option_t power_option)
{
    // 初始化相关变量
    ready_voltage = 0;
    ready_current = 0;
    PPS_voltage_next = 0;
    PPS_current_next = 0;
    status_initialized = 0;
    status_src_cap_received = 0;
    status_power = STATUS_POWER_NA;
    time_polling = 0;
    time_wait_src_cap = 0;
    time_wait_ps_rdy = 0;
    time_PPS_request = 0;
    get_src_cap_retry_count = 0;
    wait_src_cap = 0;
    wait_ps_rdy = 0;
    send_request = 0;

    memset(&FUSB302, 0, sizeof(FUSB302_dev_t));
    memset(&protocol, 0, sizeof(PD_protocol_t));
    fusb302PortInit();
    pu_log_init(PD_LOG_LEVEL_INFO);
    FUSB302.i2c_address = 0x22;
    FUSB302.i2c_read = fusb302I2cRead;
    FUSB302.i2c_write = fusb302I2cWrite;
    FUSB302.delay_ms = puDelayMs;
    if (FUSB302_init(&FUSB302) == FUSB302_SUCCESS && FUSB302_get_ID(&FUSB302, 0, 0) == FUSB302_SUCCESS)
    {
        status_initialized = 1;
    }
    else
    {
        return E_PD_UFP_ERR_INIT;
    }
    // Two stage startup for PPS Voltge < 5V
    if (PPS_voltage && PPS_voltage < PPS_V(5.0))
    {
        PPS_voltage_next = PPS_voltage;
        PPS_current_next = PPS_current;
        PPS_voltage = PPS_V(5.0);
    }
    PD_protocol_init(&protocol);
    PD_protocol_set_power_option(&protocol, power_option);
    PD_protocol_set_PPS(&protocol, PPS_voltage, PPS_current, false);
    pu_status_log_event(STATUS_LOG_DEV, NULL);

    return E_PD_UFP_OK;
}

void run(void)
{
    if (timer() || (fusb302IntPin() == 0))
    {
        FUSB302_event_t FUSB302_events = 0;
        for (uint8_t i = 0; i < 3 && FUSB302_alert(&FUSB302, &FUSB302_events) != FUSB302_SUCCESS; i++)
        {
        }
        if (FUSB302_events)
        {
            handle_FUSB302_event(FUSB302_events);
        }
    }
}
uint8_t set_PPS(uint16_t PPS_voltage, uint8_t PPS_current)
{
    if (status_power == STATUS_POWER_PPS && PD_protocol_set_PPS(&protocol, PPS_voltage, PPS_current, true))
    {
        send_request = 1;
        return true;
    }
    return false;
}

void set_power_option(enum PD_power_option_t power_option)
{
    if (PD_protocol_set_power_option(&protocol, power_option))
    {
        send_request = 1;
    }
}

void handle_protocol_event(PD_protocol_event_t events)
{
    if (events & PD_PROTOCOL_EVENT_SRC_CAP)
    {
        wait_src_cap = 0;
        get_src_cap_retry_count = 0;
        wait_ps_rdy = 1;
        time_wait_ps_rdy = puClockMs();
        pu_status_log_event(STATUS_LOG_SRC_CAP, NULL);
    }
    if (events & PD_PROTOCOL_EVENT_REJECT)
    {
        if (wait_ps_rdy)
        {
            wait_ps_rdy = 0;
            pu_status_log_event(STATUS_LOG_POWER_REJECT, NULL);
        }
    }
    if (events & PD_PROTOCOL_EVENT_PS_RDY)
    {
        PD_power_info_t p;
        uint8_t selected_power = PD_protocol_get_selected_power(&protocol);
        PD_protocol_get_power_info(&protocol, selected_power, &p);
        wait_ps_rdy = 0;
        if (p.type == PD_PDO_TYPE_AUGMENTED_PDO)
        {
            // PPS mode
            FUSB302_set_vbus_sense(&FUSB302, 0);
            if (PPS_voltage_next)
            {
                // Two stage startup for PPS voltage < 5V
                PD_protocol_set_PPS(&protocol, PPS_voltage_next, PPS_current_next, false);
                PPS_voltage_next = 0;
                send_request = 1;
                pu_status_log_event(STATUS_LOG_POWER_PPS_STARTUP, NULL);
            }
            else
            {
                time_PPS_request = puClockMs();
                status_power_ready(STATUS_POWER_PPS,
                                   PD_protocol_get_PPS_voltage(&protocol), PD_protocol_get_PPS_current(&protocol));
                pu_status_log_event(STATUS_LOG_POWER_READY, NULL);
            }
        }
        else
        {
            FUSB302_set_vbus_sense(&FUSB302, 1);
            status_power_ready(STATUS_POWER_TYP, p.max_v, p.max_i);
            pu_status_log_event(STATUS_LOG_POWER_READY, NULL);
        }
    }
}
void handle_FUSB302_event(FUSB302_event_t events)
{
    if (events & FUSB302_EVENT_DETACHED)
    {
        PD_protocol_reset(&protocol);
        return;
    }
    if (events & FUSB302_EVENT_ATTACHED)
    {
        uint8_t cc1 = 0, cc2 = 0, cc = 0;
        FUSB302_get_cc(&FUSB302, &cc1, &cc2);
        PD_protocol_reset(&protocol);
        if (cc1 && cc2 == 0)
        {
            cc = cc1;
        }
        else if (cc2 && cc1 == 0)
        {
            cc = cc2;
        }
        /* TODO: handle no cc detected error */
        if (cc > 1)
        {
            wait_src_cap = 1;
        }
        else
        {
            set_default_power();
        }
        pu_status_log_event(STATUS_LOG_CC, NULL);
    }
    if (events & FUSB302_EVENT_RX_SOP)
    {
        PD_protocol_event_t protocol_event = 0;
        uint16_t header;
        uint32_t obj[7];
        FUSB302_get_message(&FUSB302, &header, obj);
        PD_protocol_handle_msg(&protocol, header, obj, &protocol_event);
        pu_status_log_event(STATUS_LOG_MSG_RX, obj);
        if (protocol_event)
        {
            handle_protocol_event(protocol_event);
        }
    }
    if (events & FUSB302_EVENT_GOOD_CRC_SENT)
    {
        uint16_t header;
        uint32_t obj[7];
        puDelayMs(2); /* Delay respond in case there are retry messages */
        if (PD_protocol_respond(&protocol, &header, obj))
        {
            pu_status_log_event(STATUS_LOG_MSG_TX, obj);
            FUSB302_tx_sop(&FUSB302, header, obj);
        }
    }
}

uint8_t timer(void)
{
    uint16_t t = puClockMs();
    if (wait_src_cap && t - time_wait_src_cap > t_TypeCSinkWaitCap)
    {
        time_wait_src_cap = t;
        if (get_src_cap_retry_count < 3)
        {
            uint16_t header;
            get_src_cap_retry_count += 1;
            /* Try to request soruce capabilities message (will not cause power cycle VBUS) */
            PD_protocol_create_get_src_cap(&protocol, &header);
            pu_status_log_event(STATUS_LOG_MSG_TX, NULL);
            FUSB302_tx_sop(&FUSB302, header, 0);
        }
        else
        {
            get_src_cap_retry_count = 0;
            /* Hard reset will cause the source power cycle VBUS. */
            FUSB302_tx_hard_reset(&FUSB302);
            PD_protocol_reset(&protocol);
        }
    }
    if (wait_ps_rdy)
    {
        if (t - time_wait_ps_rdy > t_RequestToPSReady)
        {
            wait_ps_rdy = 0;
            set_default_power();
        }
    }
    else if (send_request || (status_power == STATUS_POWER_PPS && t - time_PPS_request > t_PPSRequest))
    {
        wait_ps_rdy = 1;
        send_request = 0;
        time_PPS_request = t;
        uint16_t header;
        uint32_t obj[7];
        /* Send request if option updated or regularly in PPS mode to keep power alive */
        PD_protocol_create_request(&protocol, &header, obj);
        pu_status_log_event(STATUS_LOG_MSG_TX, obj);
        time_wait_ps_rdy = puClockMs();
        FUSB302_tx_sop(&FUSB302, header, obj);
    }
    if (t - time_polling > t_PD_POLLING)
    {
        time_polling = t;
        return true;
    }
    return false;
}

void set_default_power(void)
{
    status_power_ready(STATUS_POWER_TYP, PD_V(5), PD_A(1));
    pu_status_log_event(STATUS_LOG_POWER_READY, NULL);
}
void status_power_ready(status_power_t status, uint16_t voltage, uint16_t current)
{
    ready_voltage = voltage;
    ready_current = current;
    status_power = status;
}
